package com.dctp.ems.controller.bak;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dctp.common.cache.ContractCache;
import com.dctp.common.cache.ProfitStatCache;
import com.dctp.common.cache.UserCache;
import com.dctp.common.controller.BaseController;
import com.dctp.common.db.UserDb;
import com.dctp.common.service.TradeService;
import com.dctp.common.utils.CollectUtil;
import com.dctp.common.utils.DateUtil;
import com.dctp.common.utils.StringUtil;
import com.dctp.common.vo.FundChangeType;
import com.dctp.common.vo.JsonResult;
import com.dctp.common.vo.PageResult;
import com.dctp.common.vo.ProfitStat;
import com.jfinal.aop.Before;
import com.jfinal.core.NotAction;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.plugin.activerecord.tx.Tx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CompensateController extends BaseController {

    private static ExecutorService es = Executors.newFixedThreadPool(1);
    private static final Logger logger = LoggerFactory.getLogger(CompensateController.class);
    private Integer getCompensateQuantity(String symbol,Long userId) {
        ProfitStat profitStat = ProfitStatCache.get(userId);
        if (null == profitStat) {
            renderJson(JsonResult.failWithMessage("用户权益统计异常"));
            return 0;
        }

        BigDecimal initEquity = profitStat.getInitEquity();
        Record config = Db.findFirst(
                "SELECT id, down_limit, up_limit, quantity " +
                        "FROM dctp_compensate_config " +
                        "WHERE symbol = ? " +
                        "AND ? >= down_limit " +
                        "AND ? < up_limit " +
                        "ORDER BY down_limit DESC " +
                        "LIMIT 1",
                symbol, initEquity, initEquity
        );

        if (config != null) {
            return config.getInt("quantity");
        } else {
            return 0;
        }
    }

    @SuppressWarnings("unchecked")
    private void handleSingle(Long userId, String symbol,Integer direction, BigDecimal avgCompensateAmount, BigDecimal totalLoss) {

        Integer count = Db.queryInt("select count(*) from dctp_order where user_id = ? and symbol = ? and direction = ? and compensate = 0  ", userId,symbol,direction);
        if (null == count || count == 0) {
            throw new RuntimeException("该用户没有可补仓的订单");
        }

        Integer quantity = getCompensateQuantity(symbol, userId);
        if (null == quantity || quantity == 0) {
            throw new RuntimeException("改用户可补手数为0");
        }

        BigDecimal totalCompensateAmount = avgCompensateAmount.multiply(BigDecimal.valueOf(quantity));
        if (totalLoss.abs().compareTo(totalCompensateAmount) < 0) {
            totalCompensateAmount = totalLoss.abs();
        }

        if (totalCompensateAmount.compareTo(BigDecimal.ZERO) > 0) {

            Map<String,Object> whereAndParams = getWhereAndParams(true);
            List<Object> params = (List<Object>) whereAndParams.get("params");
            StringBuilder where = (StringBuilder) whereAndParams.get("where");
            where.append(" and user_id = ? and symbol = ? and direction = ? ");
            params.add(userId);
            params.add(symbol);
            params.add(direction);
            int r = Db.update("UPDATE dctp_order SET compensate = 1, update_time = now() " + where,params.toArray());
            if (r == 0 ) {
                throw new RuntimeException("Failed to update order compensate status for order");
            }
        }

        // 检查用户是否存在
        Record user = UserDb.findBasicById(userId);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        // 更新用户余额
        int r = Db.update("UPDATE dctp_user SET balance = balance + ? WHERE id = ? ", totalCompensateAmount, userId);
        if (r != 1) {
            throw new RuntimeException("Failed to update user balance for user: " + userId);
        }
        // 记录赔付记录
        BigDecimal balance = user.getBigDecimal("balance");

        //写入资金明细
        TradeService.saveFundFlow(user, FundChangeType.COMEPENSATE,balance,balance.add(totalCompensateAmount),totalCompensateAmount,userId,"补仓");


    }

    @Before(Tx.class)
    public void compensate() {
        Record body = jsonBody();
        String symbol = body.getStr("symbol");
        if (StringUtil.isEmpty(symbol)) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }
        Long userId = body.getLong("user_id");
        if (null == userId) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }
        BigDecimal avgCompensateAmount = body.getBigDecimal("amount");
        if (null == avgCompensateAmount) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }


        Integer direction = body.getInt("direction");
        if (null == direction) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }

        BigDecimal totalLoss = body.getBigDecimal("totalLoss");
        if (null == totalLoss) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }

        this.handleSingle(userId,symbol,direction,avgCompensateAmount,totalLoss);
        renderJson(JsonResult.success());
    }


    /***
     * 批量补仓
     */
    @Before(Tx.class)
    public void batchCompensate() {

        Record body = jsonBody();
        String userData = body.getStr("userData");
        if (StringUtil.isEmpty(userData)) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }

        JSONArray users = JSONArray.parseArray(userData);
        if (null == users || users.isEmpty()) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }

        Integer direction = body.getInt("direction");
        if (null == direction) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }

        for (Object user : users) {
            JSONObject obj = (JSONObject) user;
            this.handleSingle(
                    obj.getLong("user_id"),
                    obj.getString("symbol"),
                    direction,
                    obj.getBigDecimal("amount"),
                    ((JSONObject) user).getBigDecimal("totalLoss"));
        }
        renderJson(JsonResult.success());
    }


    @SuppressWarnings("unchecked")
    public void allCompensate() {

        Map<String,Object> whereAndParams = getWhereAndParams(false);
        List<Object> params = (List<Object>) whereAndParams.get("params");
        StringBuilder where = (StringBuilder) whereAndParams.get("where");

        // 构造完整 SQL（不分页）
        String select = "SELECT " +
                "SUM(realized_pnl) AS total_pnl, " +
                "SUM(quantity) AS total_quantity, " +
                "SUM(open_service_fee + close_service_fee) AS total_service_fee, " +
                "SUM(margin_occupied) AS total_margin_occupied, " +
                "SUM(CASE WHEN realized_pnl < 0 THEN quantity ELSE 0 END) AS total_loss_quantity, " +
                "ABS(SUM(CASE WHEN realized_pnl < 0 THEN realized_pnl ELSE 0 END)) AS total_loss_amount, " +
                "ROUND(ABS(SUM(CASE WHEN realized_pnl < 0 THEN realized_pnl ELSE 0 END)) / NULLIF(SUM(CASE WHEN realized_pnl < 0 THEN quantity ELSE 0 END), 0), 2) AS avg_loss_per_quantity, " +
                "SUM(CASE WHEN realized_pnl > 0 THEN quantity ELSE 0 END) AS total_profit_quantity, " +
                "SUM(CASE WHEN realized_pnl > 0 THEN realized_pnl ELSE 0 END) AS total_profit_amount, " +
                "ROUND(SUM(CASE WHEN realized_pnl > 0 THEN realized_pnl ELSE 0 END) / NULLIF(SUM(CASE WHEN realized_pnl > 0 THEN quantity ELSE 0 END), 0), 2) AS avg_profit_per_quantity, " +
                "symbol, user_id ";

        String from = "FROM dctp_order " + where + " GROUP BY user_id, symbol HAVING SUM(realized_pnl) < 0";
        List<Record> list = Db.find(select + from, params.toArray());

        Integer direction = getParaToInt("direction");
        if (null == direction) {
            renderJson(JsonResult.failWithMessage("参数缺失"));
            return;
        }

        Integer others = getParaToInt("others",0);
        if (CollectUtil.isNotEmpty(list)) {
            es.execute(() -> {
                for (Record r : list) {
                    Db.tx(()-> {
                        try {

                            String symbol = r.getStr("symbol");
                            Long uid = r.getLong("user_id");
                            BigDecimal avg_loss_per_quantity = r.getBigDecimal("avg_loss_per_quantity");
                            if (avg_loss_per_quantity == null) {
                                avg_loss_per_quantity = BigDecimal.ZERO;
                            }

                            BigDecimal avg_profit_per_quantity = r.getBigDecimal("avg_profit_per_quantity");
                            if (avg_profit_per_quantity == null) {
                                avg_profit_per_quantity = BigDecimal.ZERO;
                            }

                            BigDecimal avgCompensate = avg_loss_per_quantity;
                            if (others == 0) {
                                avgCompensate = avg_loss_per_quantity.subtract(avg_profit_per_quantity);
                            }
                            this.handleSingle(uid,symbol,direction,avgCompensate,r.getBigDecimal("total_pnl"));
                            return true;
                        }catch (Exception e) {
                            logger.error(e.getMessage(),e);
                            return false;
                        }
                    });
                }
            });
        }

        renderJson(JsonResult.success());
    }


    @SuppressWarnings("unchecked")
    public void stat() {

        int page = getInt("currentPage", 1);
        int pageSize = getInt("pageSize", 10);

        Map<String,Object> whereAndParams = getWhereAndParams(false);
        List<Object> params = (List<Object>) whereAndParams.get("params");
        StringBuilder where = (StringBuilder) whereAndParams.get("where");

        // 构造最终 SQL
        String select = "SELECT " +
                "SUM(realized_pnl) AS total_pnl, " +
                "SUM(quantity) AS total_quantity, " +
                "SUM(open_service_fee + close_service_fee) AS total_service_fee, " +
                "SUM(margin_occupied) AS total_margin_occupied, " +
                "SUM(CASE WHEN realized_pnl < 0 THEN quantity ELSE 0 END) AS total_loss_quantity, " +
                "ABS(SUM(CASE WHEN realized_pnl < 0 THEN realized_pnl ELSE 0 END)) AS total_loss_amount, " +
                "ROUND(ABS(SUM(CASE WHEN realized_pnl < 0 THEN realized_pnl ELSE 0 END)) / NULLIF(SUM(CASE WHEN realized_pnl < 0 THEN quantity ELSE 0 END), 0), 2) AS avg_loss_per_quantity, " +

                "SUM(CASE WHEN realized_pnl > 0 THEN quantity ELSE 0 END) AS total_profit_quantity, " +
                "SUM(CASE WHEN realized_pnl > 0 THEN realized_pnl ELSE 0 END) AS total_profit_amount, " +
                "ROUND(SUM(CASE WHEN realized_pnl > 0 THEN realized_pnl ELSE 0 END) / NULLIF(SUM(CASE WHEN realized_pnl > 0 THEN quantity ELSE 0 END), 0), 2) AS avg_profit_per_quantity, " +

                "symbol, user_id,compensate ";


        String from  = "FROM dctp_order " + where + " GROUP BY user_id, symbol HAVING SUM(realized_pnl) < 0 ";



        Integer others = getParaToInt("others",0);
        var pageData = Db.paginate(page,pageSize,true,select,from,params.toArray());
        if (CollectUtil.isNotEmpty(pageData.getList())) {

            List<String> searchRanges = new ArrayList<>();
            String searchTimeList = getPara("searchTimeList");
            if (StringUtil.isNotEmpty(searchTimeList)) {
                searchRanges.addAll(Arrays.asList(searchTimeList.split(",")));
            }


            for (Record r : pageData.getList()) {

                Long uid = r.getLong("user_id");
                String symbol = r.getStr("symbol");

                List<Integer> activeFlags = new ArrayList<>();
                for (String range : searchRanges) {
                    String[] times = range.split("@");
                    if (times.length == 2) {
                        LocalDateTime start = DateUtil.parse(times[0].trim(), "yyyy-MM-dd HH:mm:ss");
                        LocalDateTime end = DateUtil.parse(times[1].trim(), "yyyy-MM-dd HH:mm:ss");
                        Long count = Db.queryLong(
                                "SELECT COUNT(*) FROM dctp_order WHERE user_id = ? AND symbol = ? AND create_time BETWEEN ? AND ?",
                                uid, symbol, start, end
                        );
                        activeFlags.add(count > 0 ? 1 : 0);
                    } else {
                        activeFlags.add(0); // 格式不对默认 0
                    }
                }
                Integer compensateQuantity = getCompensateQuantity(symbol,uid) * activeFlags.size();
                Integer total_loss_quantity = r.getInt("total_loss_quantity");
                if (null == total_loss_quantity) total_loss_quantity = 0;

                if (compensateQuantity.intValue() > total_loss_quantity.intValue()) {
                    compensateQuantity = total_loss_quantity;
                }
                r.set("compensateQuantity",compensateQuantity);

                BigDecimal avg_loss_per_quantity = r.getBigDecimal("avg_loss_per_quantity");
                if (avg_loss_per_quantity == null) {
                    avg_loss_per_quantity = BigDecimal.ZERO;
                }
                BigDecimal avg_profit_per_quantity = r.getBigDecimal("avg_profit_per_quantity");
                if (null == avg_profit_per_quantity) {
                    avg_profit_per_quantity = BigDecimal.ZERO;
                }

                BigDecimal avgCompensate = avg_loss_per_quantity;
                if (others == 0) {
                    avgCompensate = avg_loss_per_quantity.subtract(avg_profit_per_quantity);
                }
                r.set("avg_compensate",avgCompensate);
                r.set("symbol_name", ContractCache.getNameBySymbol(r.getStr("symbol")));
                Record user = UserCache.getBasic(uid);
                if (null != user) {
                    r.set("user",UserCache.getShowInfo(uid));
                    String key = getGroupByIdKey();
                    Long orgId = user.getLong(key);
                    if (null != orgId) {
                        Record showInfo = UserCache.getShowInfo(orgId);
                        r.set("orgInfo", showInfo);
                    }
                }
            }
        }
        renderJson(JsonResult.successWithData(
                new PageResult<>(pageData.getList(), pageData.getTotalRow(), pageSize, page)
        ));
    }

    @NotAction
    private Map<String,Object> getWhereAndParams(boolean isCompensate) {

        StringBuilder where = new StringBuilder(" WHERE status = 2 ");
        List<Object> params = new ArrayList<>();

        Record body = jsonBody();

        if (!isCompensate) {
            // symbol 筛选
            String symbol = getPara("symbol");
            if (StringUtil.isNotEmpty(symbol)) {
                where.append(" AND symbol = ?");
                params.add(symbol);
            }

            String userId = getPara("user_id");
            if (StringUtil.isNotEmpty(userId)) {
                where.append(" AND user_id = ?");
                params.add(userId);
            }

            Integer direction = getParaToInt("direction");
            if (null != direction) {
                where.append(" AND direction = ?");
                params.add(direction);
            }
        }


        // 平仓时间筛选
        String closeTime = "";
        if (isCompensate) {
            closeTime = body.getStr("close_time");
        }else {
            closeTime = getPara("close_time");
        }
        if (StringUtil.isNotEmpty(closeTime)) {
            String closeBeginTime = closeTime.split("@")[0];
            String closeEndTime = closeTime.split("@")[1];

            if (StringUtil.isNotEmpty(closeBeginTime)) {
                where.append(" AND close_time >= ?");
                params.add(DateUtil.parse(closeBeginTime, "yyyy-MM-dd HH:mm:ss"));
            }
            if (StringUtil.isNotEmpty(closeEndTime)) {
                where.append(" AND close_time <= ?");
                params.add(DateUtil.parse(closeEndTime, "yyyy-MM-dd HH:mm:ss"));
            }
        }


        // 活动时间段 openTimeList 生成（用于判断开仓时间是否在范围内）
        Integer compensate = null;
        if (isCompensate) {
            compensate = body.getInt("compensate");
        }else {
            compensate = getParaToInt("compensate");
        }
        if (null != compensate) {
            where.append(" AND compensate = ?");
            params.add(compensate);
        }

        String roleKey = "";
        Long roleUserId = null;
        if (isCompensate) {
            roleKey = body.getStr("roleKey");
            roleUserId = body.getLong("roleUserId");
        }else {
            roleKey = getPara("roleKey");
            roleUserId = getParaToLong("roleUserId");
        }
        if (StringUtil.isNotEmpty(roleKey) && null != roleUserId) {
            where.append(" AND ").append(roleKey).append(" = ?");
            params.add(roleUserId);
        }

        //查询create_time在时间范围内
        String searchTimeList = "";
        if (isCompensate) {
            searchTimeList = body.getStr("searchTimeList");
        }else {
            searchTimeList = getPara("searchTimeList");
        }
        if (StringUtil.isNotEmpty(searchTimeList)) {
            String[] ranges = searchTimeList.split(",");
            List<String> timeConditions = new ArrayList<>();

            for (String range : ranges) {
                String[] times = range.trim().split("@");
                if (times.length == 2 &&
                        StringUtil.isNotEmpty(times[0]) &&
                        StringUtil.isNotEmpty(times[1])) {

                    timeConditions.add("(create_time >= ? AND create_time <= ?)");
                    params.add(DateUtil.parse(times[0], "yyyy-MM-dd HH:mm:ss"));
                    params.add(DateUtil.parse(times[1], "yyyy-MM-dd HH:mm:ss"));
                }
            }

            if (!timeConditions.isEmpty()) {
                where.append(" AND (");
                where.append(String.join(" OR ", timeConditions));
                where.append(")");
            }
        }
        return Map.of("where",where,"params",params);
    }
}
